﻿// <copyright file="Vector.cs" company="Signal Processing Lab, Wuhan University">
// Copyright (c) 2009-2010 All Right Reserved
// </copyright>
// <author>Xinping Deng</author>
// <email>dentoby@gmail.com</email>
// <date>2010-4</date>
// <summary>This is part of Image.NET project.</summary>

namespace SPLWHU.Core.Raster
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using SPLWHU.Core.Exceptions;

    /// <summary>
    /// The vector class. A vector stores an array of values and provides the overload arighmetic operators.
    /// </summary>
    /// <typeparam name="T">The type of data in the vector.</typeparam>
    public class Vector<T>
    {
        /// <summary>
        /// The data buffer.
        /// </summary>
        private T[] data;

        /// <summary>
        /// The length of the vector.
        /// </summary>
        private int length;

        /// <summary>
        /// Initializes a new instance of the <see cref="Vector&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="length">The length of the vector.</param>
        public Vector(int length)
        {
            this.data = new T[length];
            this.length = length;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Vector&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="data">The data to initialize the vector.</param>
        public Vector(T[] data)
        {
            this.data = data;
            this.length = data.Length;
        }

        /// <summary>
        /// Gets the vector data.
        /// </summary>
        /// <value>The vector data.</value>
        public T[] Data
        {
            get { return this.data; }
        }

        /// <summary>
        /// Gets the length.
        /// </summary>
        /// <value>The length.</value>
        public int Length
        {
            get { return this.length; }
        }

        /// <summary>
        /// Gets or sets the data at the specified index.
        /// </summary>
        /// <param name="index">The position.</param>
        /// <returns>The data at <c>index</c></returns>
        public T this[int index]
        {
            get { return this.data[index]; }
            set { this.data[index] = value; }
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="operand1">The oprand1.</param>
        /// <param name="operand2">The oprand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Vector<T> operator +(Vector<T> operand1, T operand2)
        {
            // computes the results.
            T[] arr = Operator.Addition<T, T, T>(operand1.data, operand2);

            // returns
            return new Vector<T>(arr);
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Vector<T> operator +(T operand1, Vector<T> operand2)
        {
            return operand2 + operand1;
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="operand1">The oprand1.</param>
        /// <param name="operand2">The oprand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Vector<T> operator +(Vector<T> operand1, Vector<T> operand2)
        {
            T[] arr = Operator.Addition<T, T, T>(operand1.data, operand2.data);
            return new Vector<T>(arr);
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Vector<T> operator -(Vector<T> operand1, T operand2)
        {
            // converts to doubles.
            T[] arr = Operator.Subtraction<T, T, T>(operand1.data, operand2);

            // returns
            return new Vector<T>(arr);
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Vector<T> operator -(T operand1, Vector<T> operand2)
        {
            // converts to doubles.
            T[] arr = Operator.Subtraction<T, T, T>(operand1, operand2.data);

            // returns
            return new Vector<T>(arr);
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Vector<T> operator -(Vector<T> operand1, Vector<T> operand2)
        {
            // checks the dimensions.
            T[] arr = Operator.Subtraction<T, T, T>(operand1.data, operand2.data);

            // returns
            return new Vector<T>(arr);
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Vector<T> operator *(Vector<T> operand1, T operand2)
        {
            // converts to doubles.
            T[] arr = Operator.Multiply<T, T, T>(operand1.data, operand2);

            // returns
            return new Vector<T>(arr);
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Vector<T> operator *(T operand1, Vector<T> operand2)
        {
            return operand2 * operand1;
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Vector<T> operator *(Vector<T> operand1, Vector<T> operand2)
        {
            // converts to doubles.
            T[] arr = Operator.Multiply<T, T, T>(operand1.data, operand2.data);

            // returns
            return new Vector<T>(arr);
        }

        /// <summary>
        /// Implements the operator /.
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Vector<T> operator /(Vector<T> operand1, T operand2)
        {
            // converts to doubles.
            T[] arr = Operator.Division<T, T, T>(operand1.data, operand2);

            // returns
            return new Vector<T>(arr);
        }

        /// <summary>
        /// Implements the operator /.
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Vector<T> operator /(Vector<T> operand1, Vector<T> operand2)
        {
            // checks the dimensions.
            T[] arr = Operator.Division<T, T, T>(operand1.data, operand2.data);

            // returns
            return new Vector<T>(arr);
        }

        /// <summary>
        /// Implements the operator %.
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Vector<T> operator %(Vector<T> operand1, T operand2)
        {
            // converts to doubles.
            T[] arr = Operator.Modulus<T, T, T>(operand1.data, operand2);

            // returns
            return new Vector<T>(arr);
        }

        /// <summary>
        /// Implements the operator %.
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Vector<T> operator %(T operand1, Vector<T> operand2)
        {
            // converts to doubles.
            T[] arr = Operator.Modulus<T, T, T>(operand1, operand2.data);

            // returns
            return new Vector<T>(arr);
        }

        /// <summary>
        /// Implements the operator %.
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Vector<T> operator %(Vector<T> operand1, Vector<T> operand2)
        {
            // checks the dimensions.
            T[] arr = Operator.Modulus<T, T, T>(operand1.data, operand2.data);

            // returns
            return new Vector<T>(arr);
        }

        /// <summary>
        /// Performs an implicit conversion from Vector to T[].
        /// </summary>
        /// <param name="original">The vector.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator T[](Vector<T> original)
        {
            return original.data;
        }

        /// <summary>
        /// Performs an implicit conversion from T[] to Vector.
        /// </summary>
        /// <param name="data">The vector data.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Vector<T>(T[] data)
        {
            return new Vector<T>(data);
        }

        /// <summary>
        /// Concatenates the specified vectors.
        /// </summary>
        /// <param name="subs">The sub vector.</param>
        /// <returns>The concatenate vector.</returns>
        public static Vector<T> Concatenate(params Vector<T>[] subs)
        {
            int numVct = subs.Length, numVal = 0;
            for (int i = 0; i < numVct; i++)
            {
                numVal += subs[i].length;
            }

            Vector<T> concat = new Vector<T>(numVal);
            for (int i = 0, idx = 0; i < numVct; i++)
            {
                for (int j = 0, cnt = subs[i].length; j < cnt; j++, idx++)
                {
                    concat.data[idx] = subs[i].data[j];
                }
            }

            return concat;
        }

        /// <summary>
        /// Get the sub of this vector. 
        /// </summary>
        /// <param name="start">The start index.</param>
        /// <param name="length">The length.</param>
        /// <returns>The sub vector.</returns>
        public Vector<T> Sub(int start, int length)
        {
            // does the check.
            if (start + length >= this.length)
            {
                throw new VectorException("Require range {0}-{1} from {2}.", start, start + length - 1, this.ToString());
            }

            // fills data.
            Vector<T> sub = new Vector<T>(length);
            for (int i = 0; i < length; i++)
            {
                sub.data[i] = this.data[start + i];
            }

            // returns.
            return sub;
        }

        /// <summary>
        /// Clones this instance.
        /// </summary>
        /// <returns>The copy of this vector.</returns>
        public Vector<T> Clone()
        {
            Vector<T> cpy = new Vector<T>(this.data.Length);
            for (int i = 0; i < this.data.Length; i++)
            {
                cpy.data[i] = this.data[i];
            }

            return cpy;
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("Vector<{0}> [{1}]", typeof(T).ToString(), this.length);
        }
    }
}
